package com.znpc;

import com.google.ortools.Loader;
import com.google.ortools.linearsolver.MPConstraint;
import com.google.ortools.linearsolver.MPObjective;
import com.google.ortools.linearsolver.MPSolver;
import com.google.ortools.linearsolver.MPVariable;
import com.znpc.entity.Order;
import com.znpc.valmap.ValMap;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.text.DecimalFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @ClassName OrtoolsApplication
 * @Version 1.0
 * @Author young
 * @Date 2021/9/11 15:25
 * @Description 使用 Ortools 求解器求解模型
 */
public class OrtoolsApplication {

    public static void main(String[] args) throws IOException {
        solutionWithOrder0();
    }

    public static void solutionWithoutOrder0() throws IOException {
        // 获取订单列表
        List<Order> orderList = DataReader.getOrderList();

        // 获取产能数据： 产品 : { 产线 : 产能 }
        Map<String, Map<Integer, Integer>> capacityMap = DataReader.getCapacityMap();

        // 获取基料换产评分： 后序基料 : { 前序基料 : 评分 }
        Map<String, Map<String, Integer>> bmScoreMap = DataReader.getBMScoreMap();

        int ordersNumber = orderList.size();
        int linesNumber = 7;

        System.out.println("Orders number is " + ordersNumber);

        // 求解器环境加载
        System.out.println("1. 求解器准备 ......");
        Loader.loadNativeLibraries();
        MPSolver solver = MPSolver.createSolver("GLOP");

        System.out.println("2. 定义决策变量 ......");

        // 索引 k 从 0 开始，对应产线编号从 1 开始，即 k 代表 k+1 号产线
        // 决策变量 v_jk：订单 j 在 k 号产线上生产(0-不生产,1-生产)
        MPVariable[][] v_jk = new MPVariable[ordersNumber][linesNumber];
        for (int j = 0; j < ordersNumber; j++) {
            for (int k = 0; k < linesNumber; k++) {
                v_jk[j][k] = solver.makeBoolVar("v_" + j + "_" + k);
            }
        }

        // 决策变量 x_ijk：产线 k 上订单 i 是否在订单 j 前生产(0-不生产,1-生产)
        MPVariable[][][] x_ijk = new MPVariable[ordersNumber][ordersNumber][linesNumber];
        for (int i = 0; i < ordersNumber; i++) {
            for (int j = 0; j < ordersNumber; j++) {
                for (int k = 0; k < linesNumber; k++) {
                    x_ijk[i][j][k] = solver.makeBoolVar("x_" + i + "_" + j + "_" + k);
                }
            }
        }

        // 决策变量 c_j：订单 j 生产完成时间
        MPVariable[] c_j = new MPVariable[ordersNumber];
        for (int j = 0; j < ordersNumber; j++) {
            c_j[j] = solver.makeNumVar(0.0, 2000.0, "c_" + j);
        }

        // 决策变量 t_j：订单 j 超期时间
        MPVariable[] t_j = new MPVariable[ordersNumber];
        for (int j = 0; j < ordersNumber; j++) {
            t_j[j] = solver.makeNumVar(0.0, 720, "t_" + j);
        }


        System.out.println("3. 获取常量集合 ......");

        // 常量集合 d_j：订单 j 的交货日期
        Integer[] d_j = orderList.stream()
                .map(order -> order.deliverDate)
                .map(deliverDate -> ValMap.dateVal(deliverDate))
                .toArray(Integer[]::new);


        // 常量集合 e_jk：订单 j 可以分配到 k 产线生产
        int[][] e_jk = new int[ordersNumber][linesNumber];
        for (int j = 0; j < ordersNumber; j++) {
            Set<Integer> lines = capacityMap.get(orderList.get(j).pName).keySet();
            for (Integer line : lines) {
                e_jk[j][line - 1] = 1;
            }
        }

        // 常量集合 r_j：订单 j 原料到货时间
        Integer[] r_j = orderList.stream()
                .map(order -> order.arriveDate)
                .map(arriveDate -> ValMap.dateVal(arriveDate))
                .toArray(Integer[]::new);


        // 一个大的值
        int M = 9999;


        // 常量结合 p_jk：订单 j 在 k 产线上的加工时间
        float[][] p_jk = new float[ordersNumber][linesNumber];
        for (int j = 0; j < ordersNumber; j++) {
            Order order = orderList.get(j);
            Set<Integer> lines = capacityMap.get(order.pName).keySet();
            for (int k = 0; k < linesNumber; k++) {
                if (lines.contains(k + 1)) {
                    p_jk[j][k] = (float) order.number / (float) capacityMap.get(order.pName).get(k + 1);
                } else {
                    p_jk[j][k] = M;
                }
            }
        }

        // 常量集合 t_ij：订单 i 到订单 j 之前的换洗时间
        // 0-型号相同 1-型号不同
        int[][] t_ij = new int[ordersNumber][ordersNumber];
        // 常量集合 bc_ij：订单 i 到订单 j 的基料换产成本
        // 在换产评分中以订单 j 的基料为 key，获取订单 i 基料替换 j 的评分
        int[][] bc_ij = new int[ordersNumber][ordersNumber];
        // 常量集合 fc_ij：订单 i 到订单 j 的填料换产成本
        int[][] fc_ij = new int[ordersNumber][ordersNumber];
        // 常量集合 rcij：订单 i 到订单 j 的填料比例换产成本
        int[][] rcij = new int[ordersNumber][ordersNumber];
        // 常量集合 cc_ij：订单 i 到订单 j 的颜色换产成本
        int[][] cc_ij = new int[ordersNumber][ordersNumber];
        // 订单换产成本 a_ij：订单 i 到订单 j 的换产成本
        int[][] a_ij = new int[ordersNumber][ordersNumber];
        // 换产成本权重
        int w1 = 1;
        int w2 = 1;
        int w3 = 1;


        for (int i = 0; i < ordersNumber; i++) {
            Order orderI = orderList.get(i);
            for (int j = 0; j < ordersNumber; j++) {
                Order order_j = orderList.get(j);
                // 换产时间
                t_ij[i][j] = orderI.pName.equals(order_j.pName) ? 0 : 2;
                // 基料换产成本：100 - 评分
                bc_ij[i][j] = 100 - bmScoreMap.get(order_j.bm).get(orderI.bm);
                // 填料换产成本：暂时默认为 1
                fc_ij[i][j] = 1;
                // 填料比例换产成本：差的绝对值
                rcij[i][j] = Math.abs(orderI.fmRation - order_j.fmRation);
                // 颜色换产成本：0-同色 1-同层 2-不同色不同层
                if (orderI.color.equals(order_j.color)) {
                    cc_ij[i][j] = 0;
                } else if (ValMap.colorDepthVal(orderI.color) == ValMap.colorDepthVal(order_j.color)) {
                    cc_ij[i][j] = 1;
                } else {
                    cc_ij[i][j] = 2;
                }

                // 订单换产成本：a_ij = w1 * bc_ij + w2 * fc_ij + w3 * cc_ij
                a_ij[i][j] = w1 * bc_ij[i][j] + w2 * fc_ij[i][j] + w3 * cc_ij[i][j];
            }
        }

        System.out.println("4. 添加约束条件 ......");

        // 线性表达式对象
        MPConstraint mpConstraint;
        double infinity = java.lang.Double.POSITIVE_INFINITY;

        // todo 约束条件 1 紧前订单必须唯一
        for (int j = 0; j < ordersNumber; j++) {
            for (int k = 0; k < linesNumber; k++) {
                mpConstraint = solver.makeConstraint(0, 0, "c1");
                for (int i = 0; i < ordersNumber; i++) {
                    mpConstraint.setCoefficient(x_ijk[i][j][k], 1);
                }
                mpConstraint.setCoefficient(v_jk[j][k], -1);
            }
        }

        // 约束条件 2 紧后订单必须唯一
        for (int i = 0; i < ordersNumber; i++) {
            for (int k = 0; k < linesNumber; k++) {
                mpConstraint = solver.makeConstraint(-infinity, 0, "c2");
                for (int j = 0; j < ordersNumber; j++) {
                    mpConstraint.setCoefficient(x_ijk[i][j][k], 1);
                }
                mpConstraint.setCoefficient(v_jk[i][k], -1);
            }
        }

        // 约束条件 3 各产线上第一个订单至多有一个
        for (int k = 0; k < linesNumber; k++) {
            mpConstraint = solver.makeConstraint(0, 1, "c3");
            for (int j = 0; j < ordersNumber; j++) {
                mpConstraint.setCoefficient(x_ijk[0][j][k], 1);
            }
        }

        // 约束条件 4 同一产线上的订单必须在前一个紧邻订单完成后生产 #if i ！= j
        for (int i = 0; i < ordersNumber; i++) {
            for (int j = 0; j < ordersNumber; j++) {
                mpConstraint = solver.makeConstraint(t_ij[i][j] - M, infinity, "c4");
                mpConstraint.setCoefficient(c_j[j], 1);
                mpConstraint.setCoefficient(c_j[i], -1);
                for (int k = 0; k < linesNumber; k++) {
                    mpConstraint.setCoefficient(v_jk[j][k], -p_jk[j][k]);
                    mpConstraint.setCoefficient(x_ijk[i][j][k], -M);
                }
            }
        }

        // 约束条件 5 订单必须在原料到货后生产
        for (int j = 0; j < ordersNumber; j++) {
            mpConstraint = solver.makeConstraint(r_j[j], infinity, "c5");
            mpConstraint.setCoefficient(c_j[j], 1);
            for (int k = 0; k < linesNumber; k++) {
                mpConstraint.setCoefficient(v_jk[j][k], -p_jk[j][k]);
            }
        }

        // 约束条件 6 小订单超期约束
        // 约束条件 7 非小订单超期约束
        for (int j = 0; j < ordersNumber; j++) {
            if (orderList.get(j).number <= 2000) {
                mpConstraint = solver.makeConstraint(0, 24 + d_j[j], "c6");
                mpConstraint.setCoefficient(c_j[j], 1);
                mpConstraint.setCoefficient(t_j[j], 1);
            } else {
                mpConstraint = solver.makeConstraint(0, d_j[j], "c7");
                mpConstraint.setCoefficient(c_j[j], 1);
                mpConstraint.setCoefficient(t_j[j], 1);
            }
        }

        // 约束条件 8 产线分配约束
        for (int j = 0; j < ordersNumber; j++) {
            mpConstraint = solver.makeConstraint(1, 1, "c8");
            for (int k = 0; k < linesNumber; k++) {
                mpConstraint.setCoefficient(v_jk[j][k], e_jk[j][k]);
            }
        }


        // 惩罚系数：暂取值为 1
        float[] wj = new float[ordersNumber];
        for (int j = 0; j < ordersNumber; j++) {
            wj[j] = 1.0f;
        }


        System.out.println("5. 添加目标函数 ......");
        MPObjective mpObjective = solver.objective();
        // 将两个目标函数加权合并，暂取同权
        // 超期成本
        for (int j = 0; j < ordersNumber; j++) {
            mpObjective.setCoefficient(t_j[j], wj[j]);
        }
        // 换产成本
        for (int i = 0; i < ordersNumber; i++) {
            for (int j = 0; j < ordersNumber; j++) {
                for (int k = 0; k < linesNumber; k++) {
                    mpObjective.setCoefficient(x_ijk[i][j][k], a_ij[i][j]);
                }
            }
        }
        mpObjective.setMinimization();


        // 计算
        System.out.println("6. 开始计算 ......");
        long begin = System.currentTimeMillis();
        MPSolver.ResultStatus resultStatus = solver.solve();
        long end = System.currentTimeMillis();
        System.out.println("计算用时（毫秒）：" + (end - begin));
        System.out.println("MPSolver ResultStatus is " + resultStatus);
        // Check that the problem has an optimal solution.
        if (resultStatus != MPSolver.ResultStatus.OPTIMAL) {
            System.err.println("The problem does not have an optimal solution!");
            return;
        }


        System.out.println("8. 格式化结果 ......");

        System.out.println("Objective value : " + mpObjective.value());
        System.out.println("Objective value : " + solver.objective().value());

        System.out.println("完成时间 c_j:");
        for (int j = 1; j < ordersNumber; j++) {
            System.out.println("订单" + (j) + "：" + c_j[j].solutionValue() + " ");
        }
        System.out.println();

        System.out.println("超期时间 t_j:");
        for (int j = 1; j < ordersNumber; j++) {
            if (j % 20 == 0) {
                System.out.println();
            }
            System.out.print(t_j[j].solutionValue() + " ");
        }
        System.out.println();

        System.out.println("v_jk:");
        for (int j = 1; j < ordersNumber; j++) {
            System.out.print("[" + (j) + "]: ");
            for (int k = 0; k < linesNumber; k++) {
                System.out.print((int) v_jk[j][k].solutionValue() + " ");
            }
            System.out.println();
        }
        System.out.println();

        System.out.println("x_ijk:");
        for (int k = 0; k < linesNumber; k++) {
            System.out.println("x_ijk[k=" + k + "]:");
            for (int i = 0; i < ordersNumber; i++) {
                for (int j = 0; j < ordersNumber; j++) {
                    System.out.print((int) x_ijk[i][j][k].solutionValue() + " ");
                }
                System.out.println();
            }
            System.out.println();
        }
        for (int j = 0; j < ordersNumber; j++) {
            System.out.print("[" + (j) + "]: ");
            for (int k = 0; k < linesNumber; k++) {
                System.out.print((int) v_jk[j][k].solutionValue() + " ");
            }
            System.out.println();
        }
        System.out.println();
    }

    public static void solutionWithOrder0() throws IOException {
        // 获取订单列表
        List<Order> orderList = DataReader.getOrderList();

        orderList.add(0, getOrder0());

        // 获取产能数据： 产品 : { 产线 : 产能 }
        Map<String, Map<Integer, Integer>> capacityMap = DataReader.getCapacityMap();

        // 获取基料换产评分： 后序基料 : { 前序基料 : 评分 }
        Map<String, Map<String, Integer>> bmScoreMap = DataReader.getBMScoreMap();

        int ordersNumber = orderList.size();
        int linesNumber = 7;

        System.out.println("Orders number is " + ordersNumber);

        // 求解器环境加载
        System.out.println("1. 求解器准备 ......");
        Loader.loadNativeLibraries();
        MPSolver solver = MPSolver.createSolver("GLOP");

        System.out.println("2. 定义决策变量 ......");

        // 索引 k 从 0 开始，对应产线编号从 1 开始，即 k 代表 k+1 号产线
        // 决策变量 v_jk：订单 j 在 k 号产线上生产(0-不生产,1-生产)
        MPVariable[][] v_jk = new MPVariable[ordersNumber][linesNumber];
        for (int j = 1; j < ordersNumber; j++) {
            for (int k = 0; k < linesNumber; k++) {
                v_jk[j][k] = solver.makeBoolVar("v_" + j + "_" + k);
            }
        }

        // 决策变量 x_ijk：产线 k 上订单 i 是否在订单 j 前生产(0-不生产,1-生产)
        MPVariable[][][] x_ijk = new MPVariable[ordersNumber][ordersNumber][linesNumber];
        for (int i = 0; i < ordersNumber; i++) {
            for (int j = 0; j < ordersNumber; j++) {
                for (int k = 0; k < linesNumber; k++) {
                    x_ijk[i][j][k] = solver.makeBoolVar("x_" + i + "_" + j + "_" + k);
                }
            }
        }

        // 决策变量 c_j：订单 j 生产完成时间
        MPVariable[] c_j = new MPVariable[ordersNumber];
        for (int j = 1; j < ordersNumber; j++) {
            c_j[j] = solver.makeNumVar(0.0, 2000.0, "c_" + j);
        }

        // 决策变量 t_j：订单 j 超期时间
        MPVariable[] t_j = new MPVariable[ordersNumber];
        for (int j = 1; j < ordersNumber; j++) {
            t_j[j] = solver.makeNumVar(0.0, 720, "t_" + j);
        }


        System.out.println("3. 获取常量集合 ......");

        // 常量集合 d_j：订单 j 的交货日期
        Integer[] d_j = new Integer[ordersNumber];
        d_j[0] = 0;
        for (int j = 1; j < ordersNumber; j++) {
            d_j[j] = ValMap.dateVal(orderList.get(j).deliverDate);
        }
//        Integer[] d_j = orderList.stream()
//                .map(order -> order.deliverDate)
//                .map(deliverDate -> ValMap.dateVal(deliverDate))
//                .toArray(Integer[]::new);


        // 常量集合 e_jk：订单 j 可以分配到 k 产线生产
        int[][] e_jk = new int[ordersNumber][linesNumber];
        for (int j = 1; j < ordersNumber; j++) {
            Set<Integer> lines = capacityMap.get(orderList.get(j).pName).keySet();
            for (Integer line : lines) {
                e_jk[j][line - 1] = 1;
            }
        }

        // 常量集合 r_j：订单 j 原料到货时间
        Integer[] r_j = new Integer[ordersNumber];
        r_j[0] = 0;
        for (int j = 1; j < ordersNumber; j++) {
            r_j[j] = ValMap.dateVal(orderList.get(j).arriveDate);
        }
//        Integer[] r_j = orderList.stream()
//                .map(order -> order.arriveDate)
//                .map(arriveDate -> ValMap.dateVal(arriveDate))
//                .toArray(Integer[]::new);


        // 一个大的值
        int M = 9999;


        // 常量结合 p_jk：订单 j 在 k 产线上的加工时间
        float[][] p_jk = new float[ordersNumber][linesNumber];
        for (int j = 1; j < ordersNumber; j++) {
            Order order = orderList.get(j);
            Set<Integer> lines = capacityMap.get(order.pName).keySet();
            for (int k = 0; k < linesNumber; k++) {
                if (lines.contains(k + 1)) {
                    p_jk[j][k] = (float) order.number / (float) capacityMap.get(order.pName).get(k + 1);
                } else {
                    p_jk[j][k] = M;
                }
            }
        }

        // 常量集合 t_ij：订单 i 到订单 j 之前的换洗时间
        // 0-型号相同 1-型号不同
        int[][] t_ij = new int[ordersNumber][ordersNumber];
        // 常量集合 bc_ij：订单 i 到订单 j 的基料换产成本
        // 在换产评分中以订单 j 的基料为 key，获取订单 i 基料替换 j 的评分
        int[][] bc_ij = new int[ordersNumber][ordersNumber];
        // 常量集合 fc_ij：订单 i 到订单 j 的填料换产成本
        int[][] fc_ij = new int[ordersNumber][ordersNumber];
        // 常量集合 rcij：订单 i 到订单 j 的填料比例换产成本
        int[][] rcij = new int[ordersNumber][ordersNumber];
        // 常量集合 cc_ij：订单 i 到订单 j 的颜色换产成本
        int[][] cc_ij = new int[ordersNumber][ordersNumber];
        // 订单换产成本 a_ij：订单 i 到订单 j 的换产成本
        int[][] a_ij = new int[ordersNumber][ordersNumber];
        // 换产成本权重
        int w1 = 1;
        int w2 = 1;
        int w3 = 1;


        for (int i = 1; i < ordersNumber; i++) {
            Order orderI = orderList.get(i);
            for (int j = 1; j < ordersNumber; j++) {
                Order order_j = orderList.get(j);
                // 换产时间
                t_ij[i][j] = orderI.pName.equals(order_j.pName) ? 0 : 2;
                // 基料换产成本：100 - 评分
                bc_ij[i][j] = 100 - bmScoreMap.get(order_j.bm).get(orderI.bm);
                // 填料换产成本：暂时默认为 1
                fc_ij[i][j] = 1;
                // 填料比例换产成本：差的绝对值
                rcij[i][j] = Math.abs(orderI.fmRation - order_j.fmRation);
                // 颜色换产成本：0-同色 1-同层 2-不同色不同层
                if (orderI.color.equals(order_j.color)) {
                    cc_ij[i][j] = 0;
                } else if (ValMap.colorDepthVal(orderI.color) == ValMap.colorDepthVal(order_j.color)) {
                    cc_ij[i][j] = 1;
                } else {
                    cc_ij[i][j] = 2;
                }

                // 订单换产成本：a_ij = w1 * bc_ij + w2 * fc_ij + w3 * cc_ij
                a_ij[i][j] = w1 * bc_ij[i][j] + w2 * fc_ij[i][j] + w3 * cc_ij[i][j];
            }
        }

        System.out.println("4. 添加约束条件 ......");

        // 线性表达式对象
        MPConstraint mpConstraint;
        double infinity = java.lang.Double.POSITIVE_INFINITY;

        // todo 约束条件 1 紧前订单必须唯一
        for (int j = 1; j < ordersNumber; j++) {
            for (int k = 0; k < linesNumber; k++) {
                mpConstraint = solver.makeConstraint(0, 0, "c1");
                for (int i = 0; i < ordersNumber; i++) {
                    mpConstraint.setCoefficient(x_ijk[i][j][k], 1);
                }
                mpConstraint.setCoefficient(v_jk[j][k], -1);
            }
        }

        // 约束条件 2 紧后订单必须唯一
        for (int i = 1; i < ordersNumber; i++) {
            for (int k = 0; k < linesNumber; k++) {
                mpConstraint = solver.makeConstraint(-infinity, 0, "c2");
                for (int j = 1; j < ordersNumber; j++) {
                    mpConstraint.setCoefficient(x_ijk[i][j][k], 1);
                }
                mpConstraint.setCoefficient(v_jk[i][k], -1);
            }
        }

        // 约束条件 3 各产线上第一个订单至多有一个
        for (int k = 0; k < linesNumber; k++) {
            mpConstraint = solver.makeConstraint(0, 1, "c3");
            for (int j = 1; j < ordersNumber; j++) {
                mpConstraint.setCoefficient(x_ijk[0][j][k], 1);
            }
        }

        // 约束条件 4 同一产线上的订单必须在前一个紧邻订单完成后生产 #if i ！= j
        for (int i = 1; i < ordersNumber; i++) {
            for (int j = 1; j < ordersNumber; j++) {
                if (i == j) continue;
                mpConstraint = solver.makeConstraint(t_ij[i][j] - M, infinity, "c4");
                mpConstraint.setCoefficient(c_j[j], 1);
                mpConstraint.setCoefficient(c_j[i], -1);
                for (int k = 0; k < linesNumber; k++) {
                    mpConstraint.setCoefficient(v_jk[j][k], -p_jk[j][k]);
                    mpConstraint.setCoefficient(x_ijk[i][j][k], -M);
                }
            }
        }

        // 约束条件 5 订单必须在原料到货后生产
        for (int j = 1; j < ordersNumber; j++) {
            mpConstraint = solver.makeConstraint(r_j[j], infinity, "c5");
            mpConstraint.setCoefficient(c_j[j], 1);
            for (int k = 0; k < linesNumber; k++) {
                mpConstraint.setCoefficient(v_jk[j][k], -p_jk[j][k]);
            }
        }

        // 约束条件 6 小订单超期约束
        // 约束条件 7 非小订单超期约束
        for (int j = 1; j < ordersNumber; j++) {
            if (orderList.get(j).number <= 2000) {
                mpConstraint = solver.makeConstraint(0, 24 + d_j[j], "c6");
                mpConstraint.setCoefficient(c_j[j], 1);
                mpConstraint.setCoefficient(t_j[j], 1);
            } else {
                mpConstraint = solver.makeConstraint(0, d_j[j], "c7");
                mpConstraint.setCoefficient(c_j[j], 1);
                mpConstraint.setCoefficient(t_j[j], 1);
            }
        }

        // 约束条件 8 产线分配约束
        for (int j = 1; j < ordersNumber; j++) {
            mpConstraint = solver.makeConstraint(1, 1, "c8");
            for (int k = 0; k < linesNumber; k++) {
                mpConstraint.setCoefficient(v_jk[j][k], e_jk[j][k]);
            }
        }


        // 惩罚系数：暂取值为 1
        float[] wj = new float[ordersNumber];
        for (int j = 0; j < ordersNumber; j++) {
            wj[j] = 1.0f;
        }


        System.out.println("5. 添加目标函数 ......");
        MPObjective mpObjective = solver.objective();
        // 将两个目标函数加权合并，暂取同权
        // 超期成本
        for (int j = 1; j < ordersNumber; j++) {
            mpObjective.setCoefficient(t_j[j], wj[j]);
        }
        // 换产成本
        for (int i = 1; i < ordersNumber; i++) {
            for (int j = 1; j < ordersNumber; j++) {
                for (int k = 0; k < linesNumber; k++) {
                    mpObjective.setCoefficient(x_ijk[i][j][k], a_ij[i][j]);
                }
            }
        }
        mpObjective.setMinimization();


        // 计算
        System.out.println("6. 开始计算 ......");
        long begin = System.currentTimeMillis();
        MPSolver.ResultStatus resultStatus = solver.solve();
        long end = System.currentTimeMillis();
        System.out.println("计算用时（毫秒）：" + (end - begin));
        System.out.println("MPSolver ResultStatus is " + resultStatus);
        // Check that the problem has an optimal solution.
        if (resultStatus != MPSolver.ResultStatus.OPTIMAL) {
            System.err.println("The problem does not have an optimal solution!");
            return;
        }


        System.out.println("8. 格式化结果 ......");

        System.out.println("Objective value : " + mpObjective.value());
        System.out.println("Objective value : " + solver.objective().value());

//        System.out.println("完成时间 c_j:");
//        for (int j = 1; j < ordersNumber; j++) {
//            System.out.println("订单" + (j) + "：" + c_j[j].solutionValue() + " ");
//        }
//        System.out.println();
//
//        System.out.println("超期时间 t_j:");
//        for (int j = 1; j < ordersNumber; j++) {
//            if (j % 20 == 0) {
//                System.out.println();
//            }
//            System.out.print(t_j[j].solutionValue() + " ");
//        }
//        System.out.println();

//        System.out.println("v_jk:");
//        for (int j = 1; j < ordersNumber; j++) {
//            System.out.print("[" + j + "]: ");
//            for (int k = 0; k < linesNumber; k++) {
//                System.out.print((int) v_jk[j][k].solutionValue() + " ");
//            }
//            System.out.println();
//        }
//        System.out.println();

        System.out.println("x_ijk:");
        for (int k = 0; k < linesNumber; k++) {
            System.out.println("x_ijk[k=" + k + "]:");
            for (int i = 1; i < ordersNumber; i++) {
                for (int j = 1; j < ordersNumber; j++) {
                    if (i == j) {
                        System.out.println("[i,j] = [" + i + "," + j + "]" + (int) x_ijk[i][j][k].solutionValue());
                    }
                }
                System.out.println();
            }
            System.out.println();
        }


//        List<List<Integer>> machineOrders = ResultUtil.getMachineOrder(x_ijk, ordersNumber, linesNumber);
//
//        System.out.println(machineOrders.size());
//        DecimalFormat format = new DecimalFormat(".00");
//
//        for (int i = 0; i < machineOrders.size(); i++) {
//            System.out.println("machine [" + i + "]:");
//            List<Integer> orders = machineOrders.get(i);
//            System.out.println(orders.size());
//            for (int j = 0; j < orders.size(); j++) {
//                int orderId = orders.get(j);
//                System.out.print(
//                        orderId + " ("
//                                + format.format(c_j[orderId].solutionValue())
//                                + " "
//                                + format.format(t_j[orderId].solutionValue())
//                                + " ");
//
//            }
//            System.out.println();
//        }

//        System.out.println("x_ijk:");
//        for (int k = 0; k < linesNumber; k++) {
//            System.out.println("x_ijk[k=" + k + "]:");
//            for (int i = 1; i < ordersNumber; i++) {
//                for (int j = 1; j < ordersNumber; j++) {
//                    System.out.print((int) x_ijk[i][j][k].solutionValue() + " ");
//                }
//                System.out.println();
//            }
//            System.out.println();
//        }
    }

    public static Order getOrder0() {
        Order order = new Order();
        order.orderId = "J0";
        order.userId = "用友";
        order.batchId = "K20210562";
        order.arriveDate = LocalDate.of(2021, 8, 1);
        order.deliverDate = LocalDate.of(2021, 8, 1);
        order.number = 0;
        order.pName = "";
        order.color = "";
        order.bm = "";
        order.fm = "";
        order.fmRation = 0;
        order.halogen = 0;
        return order;
    }
}

